package com.example.project.utils;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.example.project.annotation.FieldMeta;

/**
 * @description V1.0 javabean 对象转换工具类 Demo class
 * 
 * @author wangxu
 * @date 2016/10/31
 */
public class EntityUtil {
    
    private static final Logger logger = LoggerFactory.getLogger(EntityUtil.class);

	/**
	 * 数组集合转化为指定对象集合 指定的实体对象必须包含所有字段的构造方法，数组的元素的顺序将和构造方法顺序和类型一一对应
	 * 
	 * @param list 集合
	 * @param clazz 目标类
	 * @param <T> 类型
	 * @return List<T>
	 * @description 用于jpa查询自定义vo用的
	 */
	public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz) {
		List<T> returnList = new ArrayList<>();
		if (list == null || list.isEmpty()) {
			return returnList;
		}
		
		Class[] c2 = null;
		Constructor[] constructors = clazz.getConstructors();
		for (Constructor<?> constructor : constructors) {
			Class[] tClass = constructor.getParameterTypes();
			if (tClass.length == list.get(0).length) {
				c2 = tClass;
				break;
			}
		}
		
		if (c2 == null) {
			logger.warn("未找到匹配的构造方法，参数数量: {}", list.get(0).length);
			return returnList;
		}
		
		// 构造方法实例化对象
		for (Object[] o : list) {
			try {
				Constructor<T> constructor = clazz.getConstructor(c2);
				returnList.add(constructor.newInstance(o));
			} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
				logger.error("对象转换失败: {}", e.getMessage());
			}
		}

		return returnList;
	}

	/**
	 * 对象类型强转
	 * 
	 * @param object 要强转的对象
	 * @param entityClass 强转后的类型
	 * @param <T> 目标类型
	 * @return T
	 */
	public static <T> T convertBean(Object object, Class<T> entityClass) {
		if (object == null) {
			return null;
		}
		try {
			return JSON.parseObject(JSON.toJSONString(object), entityClass);
		} catch (Exception e) {
			logger.error("对象转换失败: {}", e.getMessage());
			return null;
		}
	}

	/**
	 * 对象转为map
	 * 
	 * @param object 要转换的对象
	 * @return Map<String, Object>
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> objectToMap(Object object) {
		if (object == null) {
			return null;
		}
		return convertBean(object, Map.class);
	}

	/**
	 * map转换对象
	 * 
	 * @param map map集合
	 * @param t 目标类
	 * @param <T> 目标类型
	 * @return T
	 */
	public static <T> T mapToObject(Map<String, ? extends Object> map, Class<T> t) {
		if (map == null) {
			return null;
		}
		try {
			T instance = t.getDeclaredConstructor().newInstance();
			ConvertUtils.register(new DateConverter(null), java.util.Date.class);
			org.apache.commons.beanutils.BeanUtils.populate(instance, map);
			return instance;
		} catch (Exception e) {
			logger.error("Map转对象失败: {}", e.getMessage());
			return null;
		}
	}

	/**
	 * 合并对象
	 * 
	 * @param source
	 *            资源对象, target 目标对象, ignoreProperties 赋值new String[]{}
	 * @return T target对象
	 * @descprition 对象转换
	 * @version 1.0
	 */
	public static <T> T copy(Object source, Class<T> target, String... ignoreProperties) {
		T targetInstance = null;
		try {
			targetInstance = target.getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			logger.error("对象拷贝失败 - 实例化目标对象失败: {}", e.getMessage());
			return null;
		}
		
		if (targetInstance == null) {
			return null;
		}
		
		if (ArrayUtils.isEmpty(ignoreProperties)) {
			BeanUtils.copyProperties(source, targetInstance);
		} else {
			BeanUtils.copyProperties(source, targetInstance, ignoreProperties);
		}
		return targetInstance;
	}

	/**
	 * 对象深拷贝
	 * 通过序列化实现对象的深拷贝，适用于可序列化的对象
	 * 
	 * @param source 源对象
	 * @param target 目标类
	 * @param <T> 目标类型
	 * @return 深拷贝后的对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T clone(Object source, Class<T> target) {
		T info = null;
		try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝，而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(source);
			// 将流序列化成对象
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bais);
			info = (T) ois.readObject();
		} catch (IOException | ClassNotFoundException e) {
			logger.error("对象克隆失败: {}", e.getMessage());
		}

		return info;
	}

	/**
	 * @param list,
	 *            target, ignoreProperties]
	 * @return java.util.List<T>
	 * @descprition 对象list转换
	 * @version 1.0
	 */
	public static <T, E> List<T> copyList(List<E> list, Class<T> target, String... ignoreProperties) {
		List<T> targetList = new ArrayList<>();
		if (CollectionUtils.isEmpty(list)) {
			return targetList;
		}
		for (E e : list) {
			targetList.add(copy(e, target, ignoreProperties));
		}
		return targetList;
	}

	/**
	 * 对象列表转换为Map列表
	 * 
	 * @param obj 对象列表
	 * @return Map列表，如果输入不是List类型则返回null
	 */
	public static List<Map<?, ?>> objToList(Object obj) {
		List<Map<?, ?>> result = new ArrayList<Map<?, ?>>();
		if (obj instanceof List<?>) {
			for (Object o : (List<?>) obj) {
				result.add(objectToMap(o));
			}
			return result;
		}
		return null;
	}

	/**
	 * 对象列表转换为Map<String, Object>列表
	 * 
	 * @param obj 对象列表
	 * @return Map<String, Object>列表，如果输入不是List类型则返回null
	 */
	public static List<Map<String, Object>> objToListMap(Object obj) {
		List<Map<String, Object>> result = Lists.newArrayList();
		if (obj instanceof List<?>) {
			for (Object o : (List<?>) obj) {
				result.add(objectToMap(o));
			}
			return result;
		}
		return null;
	}

	/**
	 * 检查两个对象是否有差异
	 * 
	 * @param obj1 对象1
	 * @param obj2 对象2
	 * @param class1 对象类类型
	 * @return true表示有差异，false表示无差异
	 */
	public static boolean queryContrastObjStatus(Object obj1, Object obj2, Class<?> class1, String... filter) {
		boolean status = false;
		try {
			Field[] fields = class1.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				if (filter.length >= 1) {
					// 过滤：只包含指定的字段
					boolean searchContains = !Arrays.asList(filter).contains(fieldName);
					if (searchContains) {
						continue;
					}

				}
				if ("serialVersionUID".equals(field.getName())) {
					continue;
				}
				PropertyDescriptor pd = new PropertyDescriptor(field.getName(), class1);
				Method getMethod = pd.getReadMethod();
				Object o1 = getMethod.invoke(obj1);
				Object o2 = getMethod.invoke(obj2);
				String s1 = o1 == null ? "" : o1.toString();
				String s2 = o2 == null ? "" : o2.toString();
				if (!s1.equals(s2)) {
					status = true;
					break;
				}
			}
		} catch (Exception e) {
			logger.error("对象对比状态检查失败: {}", e.getMessage());
		}
		return status;
	}

	/**
	 * 对比两个对象的差异（排除指定字段）
	 * 
	 * @param obj1 对象1
	 * @param obj2 对象2
	 * @param className 对象类类型
	 * @param filter 要排除的字段名
	 * @return 差异描述字符串
	 */
	public static String queryContrastObj(Object obj1, Object obj2, Class<?> className, String... filter) {
		StringBuilder result = new StringBuilder();
		try {
			Field[] fields = className.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				// 过滤：排除指定的字段
				boolean searchContains = Arrays.asList(filter).contains(fieldName);
				if (searchContains) {
					continue;
				}
				if ("serialVersionUID".equals(fieldName)) {
					continue;
				}
				PropertyDescriptor pd = new PropertyDescriptor(fieldName, className);
				Method getMethod = pd.getReadMethod();
				Object o1 = getMethod.invoke(obj1);
				Object o2 = getMethod.invoke(obj2);
				String s1 = o1 == null ? "" : o1.toString();
				String s2 = o2 == null ? "" : o2.toString();
				if (!s1.equals(s2)) {
					result.append("不同的属性：" + fieldName + " 属性值：[" + s1 + "，" + s2 + "]；<br/>");
				}
			}
		} catch (Exception e) {
			logger.error("对象对比失败: {}", e.getMessage());
		}
		return result.toString();
	}

	/**
	 * 对比两个对象的差异（只包含指定字段）
	 * 
	 * @param obj1 对象1
	 * @param obj2 对象2
	 * @param className 对象类类型
	 * @param filter 要包含的字段名
	 * @return 差异描述字符串
	 */
	public static String queryContrastObjByFile(Object obj1, Object obj2, Class<?> className, String... filter) {
		StringBuilder result = new StringBuilder();
		try {
			Field[] fields = className.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				// 过滤：只包含指定的字段
				boolean searchContains = !Arrays.asList(filter).contains(fieldName);
				if (searchContains) {
					continue;
				}
				if ("serialVersionUID".equals(fieldName)) {
					continue;
				}
				PropertyDescriptor pd = new PropertyDescriptor(fieldName, className);
				Method getMethod = pd.getReadMethod();
				Object o1 = getMethod.invoke(obj1);
				Object o2 = getMethod.invoke(obj2);
				String s1 = o1 == null ? "" : o1.toString();
				String s2 = o2 == null ? "" : o2.toString();
				if (!s1.equals(s2)) {
					result.append("不同的属性：" + fieldName + " 属性值：[" + s1 + "，" + s2 + "]；<br/>");
				}
			}
		} catch (Exception e) {
			logger.error("对象对比失败: {}", e.getMessage());
		}
		return result.toString();
	}

	/**
	 * 对象对比 - 对比两个对象指定字段的差异
	 * 
	 * @param obj1 对象1
	 * @param obj2 对象2
	 * @param className 类类型
	 * @param includeFields 包含的字段（如果提供，则只对比这些字段）
	 * @param excludeFields 排除的字段（如果提供，则排除这些字段）
	 * @return 差异描述
	 */
	public static String compareObjects(Object obj1, Object obj2, Class<?> className, 
	                                   List<String> includeFields, List<String> excludeFields) {
		StringBuilder result = new StringBuilder();
		if (obj1 == null || obj2 == null) {
			return "对象不能为空";
		}
		
		try {
			Field[] fields = className.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				if ("serialVersionUID".equals(fieldName)) {
					continue;
				}
				
				// 字段过滤逻辑
				if (includeFields != null && !includeFields.isEmpty() && !includeFields.contains(fieldName)) {
					continue;
				}
				if (excludeFields != null && excludeFields.contains(fieldName)) {
					continue;
				}
				
				PropertyDescriptor pd = new PropertyDescriptor(fieldName, className);
				Method getMethod = pd.getReadMethod();
				Object o1 = getMethod.invoke(obj1);
				Object o2 = getMethod.invoke(obj2);
				String s1 = o1 == null ? "" : o1.toString();
				String s2 = o2 == null ? "" : o2.toString();
				
				if (!s1.equals(s2)) {
					result.append("不同的属性：" + fieldName + " 属性值：[" + s1 + "，" + s2 + "]；<br/>");
				}
			}
		} catch (Exception e) {
			logger.error("对象对比失败: {}", e.getMessage());
		}
		return result.toString();
	}

	/**
	 * 合并对象属性值<br>
	 * 如果两个相同对象中同一属性都有值，那么sourceBean中的值会覆盖targetBean的值<br>
	 * 如果sourceBean有值，targetBean没有，则采用sourceBean的值<br>
	 * 如果sourceBean没有值，targetBean有，则保留targetBean的值
	 * 
	 * @param sourceBean 被提取的对象bean
	 * @param targetBean 用于合并的对象bean
	 * @param filter 需要合并赋值字段
	 * @return targetBean,合并后的对象
	 */
	public static <T> T combineObjects(T sourceBean, T targetBean, String... filter) {
		if (sourceBean == null || targetBean == null) {
			return targetBean;
		}
		
		Class<?> sourceBeanClass = sourceBean.getClass();
		Class<?> targetBeanClass = targetBean.getClass();

		Field[] sourceFields = sourceBeanClass.getDeclaredFields();
		Field[] targetFields = targetBeanClass.getDeclaredFields();
		
		for (int i = 0; i < sourceFields.length; i++) {
			Field sourceField = sourceFields[i];
			if (Modifier.isStatic(sourceField.getModifiers())) {
				continue;
			}
			
			if (i >= targetFields.length) {
				continue;
			}
			
			Field targetField = targetFields[i];
			if (Modifier.isStatic(targetField.getModifiers())) {
				continue;
			}
			
			sourceField.setAccessible(true);
			targetField.setAccessible(true);
			try {
				String fieldName = sourceField.getName();
				if ("serialVersionUID".equals(fieldName)) {
					continue;
				}
				
				// 字段过滤
				boolean isFiltered = Arrays.asList(filter).contains(fieldName);
				if (sourceField.get(sourceBean) != null && !isFiltered) {
					targetField.set(targetBean, sourceField.get(sourceBean));
				}
			} catch (IllegalArgumentException | IllegalAccessException e) {
				logger.error("对象合并失败 - 字段: {}, 错误: {}", sourceField.getName(), e.getMessage());
			}
		}
		return targetBean;
	}
	
	/**
	 * 检查对象是否包含指定字段
	 * 
	 * @param obj 对象
	 * @param fieldName 字段名
	 * @return boolean
	 */
    public static boolean isFieldExist(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return false;
        }
        try {
            Class<?> clazz = obj.getClass();
            clazz.getDeclaredField(fieldName);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
    }
    
    /**
     * 获取对象字段值
     * 
     * @param obj 对象
     * @param fieldName 字段名
     * @return 字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return null;
        }
        try {
            Class<?> clazz = obj.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            logger.error("获取字段值失败 - 字段: {}, 错误: {}", fieldName, e.getMessage());
            return null;
        }
    }
    
    /**
     * 设置对象字段值
     * 
     * @param obj 对象
     * @param fieldName 字段名
     * @param value 字段值
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        if (obj == null || fieldName == null) {
            return;
        }
        try {
            Class<?> clazz = obj.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            logger.error("设置字段值失败 - 字段: {}, 错误: {}", fieldName, e.getMessage());
        }
    }
    
    /**
     * 获取字段上的FieldMeta注解
     * 
     * @param obj 对象
     * @param fieldName 字段名
     * @return FieldMeta注解实例
     */
    public static FieldMeta getFieldMeta(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return null;
        }
        try {
            Class<?> clazz = obj.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            return field.getAnnotation(FieldMeta.class);
        } catch (Exception e) {
            logger.error("获取字段注解失败 - 字段: {}, 错误: {}", fieldName, e.getMessage());
            return null;
        }
    }
    
    /**
     * 获取对象所有包含FieldMeta注解的字段
     * 
     * @param obj 对象
     * @return 字段名到FieldMeta注解的映射
     */
    public static Map<String, FieldMeta> getFieldMetas(Object obj) {
        Map<String, FieldMeta> metas = new HashMap<>();
        if (obj == null) {
            return metas;
        }
        
        try {
            Class<?> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                FieldMeta meta = field.getAnnotation(FieldMeta.class);
                if (meta != null) {
                    metas.put(field.getName(), meta);
                }
            }
        } catch (Exception e) {
            logger.error("获取字段注解映射失败: {}", e.getMessage());
        }
        return metas;
    }
    
    /**
     * 根据FieldMeta注解验证对象字段
     * 
     * @param obj 对象
     * @return 验证结果，空Map表示验证通过
     */
    public static Map<String, String> validateByFieldMeta(Object obj) {
        Map<String, String> errors = new HashMap<>();
        if (obj == null) {
            errors.put("global", "对象不能为空");
            return errors;
        }
        
        try {
            Class<?> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                FieldMeta meta = field.getAnnotation(FieldMeta.class);
                if (meta != null) {
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    
                    // 必填验证
                    if (meta.required() && (value == null || value.toString().trim().isEmpty())) {
                        String errorMsg = StringUtils.isNotBlank(meta.message()) ? meta.message() : 
                                        StringUtils.isNotBlank(meta.name()) ? meta.name() + "不能为空" : 
                                        field.getName() + "不能为空";
                        errors.put(field.getName(), errorMsg);
                        continue;
                    }
                    
                    // 其他验证规则可以在这里添加...
                }
            }
        } catch (Exception e) {
            logger.error("字段验证失败: {}", e.getMessage());
            errors.put("global", "验证过程发生异常");
        }
        
        return errors;
    }
}
